package com.hunantv.mglive.mqtt;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Binder;
import android.os.HandlerThread;
import android.os.IBinder;
import android.util.Log;

import com.hunantv.mglive.mqtt.data.MqttTokenData;
import com.hunantv.mglive.utils.L;

import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttTopic;
import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence;

import java.util.Calendar;
import java.util.Locale;

/**
 * mqtt消息服务
 */
public class MqttService extends Service {
    public static final String DEBUG_TAG = "MqttService"; // Log标记
    public static String MQTT_CLIENT_ID = "ChatRoom";
    private static final String MQTT_THREAD_NAME = "MqttService[" + DEBUG_TAG + "]"; // Handler Thread ID

    public static final int MQTT_QOS_0 = 0; //消息投放级别 QOS Level 0 (最多一次，有可能重复或丢失。 )
    public static final int MQTT_QOS_1 = 1; //消息投放级别 QOS Level 1 (至少一次，有可能重复。 )
    public static final int MQTT_QOS_2 = 2; //消息投放级别 QOS Level 2 (只有一次，确保消息只到达一次（用于比较严格的计费系统）。)

    public static int[] qos = {MQTT_QOS_0};//订阅级别

    private static int MQTT_KEEP_ALIVE; //心跳包时间，毫秒
    private static final String MQTT_KEEP_ALIVE_TOPIC_FORAMT = "/users/%s/keepalive"; // Topic format for KeepAlives
    private static final byte[] MQTT_KEEP_ALIVE_MESSAGE = {0}; // 心跳包发送内容
    private static final int MQTT_KEEP_ALIVE_QOS = MQTT_QOS_0; //心跳包的发送级别默认最低

    private static final String MQTT_URL_FORMAT = "tcp://%s:%d"; // 推送url格式组装

    private static final String ACTION_START = MQTT_CLIENT_ID + ".START"; // Action to start 启动
    private static final String ACTION_STOP = MQTT_CLIENT_ID + ".STOP"; // Action to stop 停止
    private static final String ACTION_KEEPALIVE = MQTT_CLIENT_ID + ".KEEPALIVE"; // Action to keep alive used by alarm manager保持心跳闹钟使用
    private static final String ACTION_RECONNECT = MQTT_CLIENT_ID + ".RECONNECT"; // Action to reconnect 重新连接
    private static final String ACTION_CHANGE_CONNECT = MQTT_CLIENT_ID + ".CHANGECONNECT"; // Action to change connect 切换连接


    private boolean mStarted = false; //推送client是否启动
    private MqttDefaultFilePersistence mDataStore; // Defaults to FileStore


    private MqttClient mClient;                    // Mqtt Client

    private AlarmManager mAlarmManager;            //闹钟
    private ConnectivityManager mConnectivityManager; //网络改变接收器

    private MqttTokenData tokenData; //token

    private MqttCallback mqttCallback;//回调接口


    /**
     * 启动推送服务
     *
     * @param ctx context to start the service with
     */
    public void actionStart(Context ctx, MqttCallback mqttCallback, MqttTokenData mqttTokenData) {
        this.tokenData = mqttTokenData;
        this.mqttCallback = mqttCallback;
        Intent i = new Intent(ctx, MqttService.class);
        i.setAction(ACTION_START);
        ctx.startService(i);
    }

    /**
     * 切换推送服务
     *
     * @param ctx context to start the service with
     */
    public void actionChangeConnect(Context ctx, MqttCallback mqttCallback, MqttTokenData mqttTokenData) {
        this.tokenData = mqttTokenData;
        this.mqttCallback = mqttCallback;
        Intent i = new Intent(ctx, MqttService.class);
        i.setAction(ACTION_CHANGE_CONNECT);
        ctx.startService(i);
    }


    /**
     * 停止推送服务
     *
     * @param ctx context to start the service with
     */
    public void actionStop(Context ctx) {
        Intent i = new Intent(ctx, MqttService.class);
        i.setAction(ACTION_STOP);
        ctx.startService(i);
    }

    /**
     * 初始化设备id和请求参数包含连接处理、数据存储、闹钟警报、网络接收器
     */
    @Override
    public void onCreate() {
        super.onCreate();
        HandlerThread thread = new HandlerThread(MQTT_THREAD_NAME);
        thread.start();
        mDataStore = new MqttDefaultFilePersistence(getMqttCacheFilePath());

        mConnectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
    }

    /**
     * 获取Mqtt消息缓存目录
     * @return
     */
    private String getMqttCacheFilePath(){

//        if(Environment.isExternalStorageEmulated() && Environment.getExternalStorageDirectory() != null){
//            File file = new File(Environment.getExternalStorageDirectory(),"MG_Mqtt");
//            if(!file.exists() || !file.isDirectory()){
//                file.mkdirs();
//            }
//            //SD卡
//            return file.getAbsolutePath();
//        }
        //内存
        return getCacheDir().getAbsolutePath();
    }

    @Override
    public boolean isRestricted() {
        return super.isRestricted();
    }

    /**
     * Service onStartCommand
     * Handles the action passed via the Intent
     * 通过意图处理服务
     *
     * @return START_REDELIVER_INTENT
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        String action = null;
        if (intent != null) {
            action = intent.getAction();
            L.d(DEBUG_TAG, "推送服务接收到一个请求" + action);

        }
        if (action == null) {
            L.d(DEBUG_TAG, "推送服务接收到的请求为null！推送服务不执行任何操作");
        } else {
            if (action.equals(ACTION_START)) {
                L.d(DEBUG_TAG, "接收到《启动》推送服务命令");
                start(tokenData, mqttCallback);
                Log.i("MQTT_CON", "---->Start");
            } else if (action.equals(ACTION_STOP)) {
                L.d(DEBUG_TAG, "接收到《停止》推送服务命令");
                this.hasTry = false;
                stop();
            } else if (action.equals(ACTION_KEEPALIVE)) {
                L.d(DEBUG_TAG, "接收到《发送心跳包》推送服务命令");
                keepAlive(tokenData, mqttCallback);
            } else if (action.equals(ACTION_RECONNECT)) {
                L.d(DEBUG_TAG, "接收到《重启》推送服务命令");
                if (isNetworkAvailable()) {
                    start(tokenData, mqttCallback);
                }
            } else if (action.equals(ACTION_CHANGE_CONNECT)) {
                L.d(DEBUG_TAG, "接收到《切换连接》推送服务命令");
                changeConnection(tokenData, mqttCallback);
            }
        }
        return START_REDELIVER_INTENT;
    }

    /**
     * 尝试启动推送服务器，并注册网络改变接收器
     */
    private synchronized void start(final MqttTokenData token, final MqttCallback callback) {
        if (mStarted && mClient != null && mClient.isConnected()) {
            L.d(DEBUG_TAG, "尝试启动推送服务，但推送服务已经启动");
            return;
        }
        if (hasScheduledKeepAlives()) {
            stopKeepAlives();
        }
        new Thread() {
            @Override
            public void run() {
                super.run();
                connect(token, callback);
            }
        }.start();
    }


    private synchronized void changeConnection(final MqttTokenData token, final MqttCallback callback) {
        if (tokenData == null) {
            L.d(DEBUG_TAG, "MQTT Token is null.");
            return;
        }
        new Thread() {
            @Override
            public void run() {
                super.run();
                stopConnection();
                startConnection(token, callback);
            }
        }.start();
    }

    /**
     * 停止推送服务
     */
    private synchronized void stop() {
        new Thread() {
            @Override
            public void run() {
                super.run();
                stopConnection();
            }
        }.start();
//        unregisterReceiver(mConnectivityReceiver);
    }

    private void stopConnection() {
        try {
            if (!mStarted) {
                L.d(DEBUG_TAG, "试图停止推送服务器但是推送服务并没有运行");
                return;
            }
            if (mClient != null) {
                mClient.disconnect();
                mClient = null;
                mStarted = false;

                stopKeepAlives();
            }
            if(mDataStore != null){
                Log.i("MQTT_CON","---->Stop");
                //释放缓存数据
                mDataStore.clear();
            }

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Connects to the broker with the appropriate datastore
     * 连接到推送服务器与适当的数据存储
     */
    private synchronized void connect(final MqttTokenData token, final MqttCallback callback) {
        if (tokenData == null) {
            L.d(DEBUG_TAG, "MQTT Token is null.");
            return;
        }
        startConnection(token, callback);
    }

    private boolean hasTry = true;

    private synchronized void startConnection(MqttTokenData token, MqttCallback callback) {
        int tryIndex = 0;//重连次数
        hasTry = true;
        do {
            try {
                L.d(DEBUG_TAG, "hasTry="+hasTry);
                String url = String.format(Locale.US, MQTT_URL_FORMAT, token.getSyringe_addr(), token.getSyringe_port());
                String deviceId = token.getClientId();
                String[] topicFilters = new String[]{token.getTopic()};
                MQTT_KEEP_ALIVE = token.getPing() * 1000;
                L.d(DEBUG_TAG, "连接推送服务器 clientId：" + deviceId + "   with URL:" + url);
                mClient = new MqttClient(url, deviceId, mDataStore);

                L.d(DEBUG_TAG, "----- creat client");
                MqttConnectOptions options = new MqttConnectOptions();
                options.setUserName(token.getAccount());
                options.setPassword(token.getPassword().toCharArray());
                L.d(DEBUG_TAG, "-----set date");
                mClient.connect(options);
                L.d(DEBUG_TAG, "-----connect");
                mClient.subscribe(topicFilters, qos);
                mClient.setCallback(callback);
                mStarted = true; // Service is now connected
                L.d(DEBUG_TAG, "成功连接推送服务器并启动心跳包闹钟");
                startKeepAlives();
//                    registerReceiver(mConnectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
                tryIndex = 0;
                hasTry = false;
            } catch (MqttException e) {
//                hasTry = true;
                L.e(DEBUG_TAG, e.getMessage(), e);
                try {
                    Thread.sleep(5000);
                    L.d(DEBUG_TAG, "连接失败");
                    tryIndex = tryIndex + 1;
                    L.d(DEBUG_TAG, "重新连接，第 " + tryIndex + "次。");
                } catch (Exception e1) {
                    L.d(DEBUG_TAG, e1.getMessage(), e1);
                }
            } catch (Exception e2) {
                L.d(DEBUG_TAG, e2.getMessage(), e2);
            }
        } while (hasTry);
    }

    /**
     * 启动心跳包闹钟
     */
    private void startKeepAlives() {
        mAlarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
        Intent i = new Intent(MqttService.this, MqttService.class);
        i.setAction(ACTION_KEEPALIVE);
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(System.currentTimeMillis());
        calendar.add(Calendar.SECOND, 1);
        PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
        mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), MQTT_KEEP_ALIVE, pi);
    }

    /**
     * 取消已经存在的闹钟
     */
    private void stopKeepAlives() {
        Intent i = new Intent();
        i.setClass(this, MqttService.class);
        i.setAction(ACTION_KEEPALIVE);
        PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
        mAlarmManager.cancel(pi);
    }

    /**
     * 发送心跳数据到服务器
     */
    private synchronized void keepAlive(final MqttTokenData token, final MqttCallback callback) {
        new Thread() {
            @Override
            public void run() {
                super.run();
                if (isConnected()) {
                    try {
                        sendKeepAlive();
                    } catch (MqttConnectivityException ex) {
                        ex.printStackTrace();
                        connect(token, callback);
                    } catch (MqttException ex) {
                        ex.printStackTrace();
                        stopConnection();
                    }
                }
            }
        }.start();
    }

    /**
     * 通过ConnectivityManager查询网络连接状态
     *
     * @return 如果网络状态正常则返回true反之flase
     */
    private boolean isNetworkAvailable() {
        NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
        return info != null && (info.isConnected() && info.isAvailable());
    }

    /**
     * 判断推送服务是否连接
     *
     * @return 如果是连接的则返回true反之false
     */
    private boolean isConnected() {
        if (mStarted && mClient != null && !mClient.isConnected()) {
            L.d(DEBUG_TAG, "判断推送服务已经断开");
        }

        return mClient != null && (mStarted && mClient.isConnected());
    }

//    /**
//     * 网络状态发生变化接收器
//     */
//    private final BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
//        @Override
//        public void onReceive(Context context, Intent intent) {
//            if (isNetworkAvailable()) {
//                Log.e(DEBUG_TAG, "网络连接发生了变化--网络连接");
//                mClient=null;
//                reconnectIfNecessary();
//            } else {
//                Log.e(DEBUG_TAG, "网络连接发生了变化--网络断开");
//                stopKeepAlives();
//                mClient = null;
//            }
//        }
//    };

    /**
     * 发送保持连接的指定的主题
     *
     * @return MqttDeliveryToken specified token you can choose to wait for completion
     */
    private synchronized MqttDeliveryToken sendKeepAlive() throws MqttConnectivityException, MqttException {
        if (!isConnected())
            throw new MqttConnectivityException();

        MqttTopic mKeepAliveTopic = mClient.getTopic(String.format(Locale.US, MQTT_KEEP_ALIVE_TOPIC_FORAMT, tokenData.getClientId()));

        if (tokenData != null) {
            L.d(DEBUG_TAG, "向服务器发送心跳包url：" + tokenData.getSyringe_addr());
        }

        MqttMessage message = null;
        try {
            message = new MqttMessage(MQTT_KEEP_ALIVE_MESSAGE);
            message.setQos(MQTT_KEEP_ALIVE_QOS);
        } catch (Exception e) {
            L.e(DEBUG_TAG, e);
        }
        if (mClient != null && mClient.isConnected()) {
            return mKeepAliveTopic.publish(message);
        } else {
            L.d(DEBUG_TAG, "心跳包连接断开");
        }
        return null;
    }

    /**
     * 查询是否已经有一个心跳包的闹钟
     *
     * @return 如果已经有一个心跳包的闹钟则返回true反之false
     */
    private synchronized boolean hasScheduledKeepAlives() {
        Intent i = new Intent();
        i.setClass(this, MqttService.class);
        i.setAction(ACTION_KEEPALIVE);
        PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, PendingIntent.FLAG_NO_CREATE);
        return (pi != null);
    }


    @Override
    public IBinder onBind(Intent arg0) {
        System.out.println("onBind.....");
        return new ServiceBinder();
    }

    public void connectionLost() {
        L.d(DEBUG_TAG, "连接断开，重新连接。");
        stopKeepAlives();
        //连接断开时,不判断网络状态直接重连
        mClient = null;
        start(tokenData, mqttCallback);
    }

    /**
     * MqttConnectivityException Exception class
     */
    private class MqttConnectivityException extends Exception {
        private static final long serialVersionUID = -7385866796799469420L;
    }

    public class ServiceBinder extends Binder {
        public MqttService getService() {
            return MqttService.this;
        }
    }
}